docker basics
install
Follow https://docs.docker.com/engine/install/ubuntu/
### ubuntu
# remove old
sudo apt-get remove docker docker-engine docker.io containerd runc
# install from repo
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg lsb-release
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
# verify
sudo docker run --rm hello-world
Or install from TUNA:
export DOWNLOAD_URL="https://mirrors.tuna.tsinghua.edu.cn/docker-ce"
curl -fsSL https://raw.githubusercontent.com/docker/docker-install/master/install.sh | sh
You can control docker daemon by:
sudo systemctl enable docker
sudo systemctl start docker
By default, only root user (sudo
) can use docker. A docker group can be created to avoid this, but anyone in the docker group equals a sudoer.
sudo groupadd docker
sudo usermod -aG docker $USER
docker hub mirrors
Add in /etc/docker/daemon.json
:
{
"registry-mirrors": [
"https://hub-mirror.c.163.com",
"https://mirror.baidubce.com"
]
}
sudo systemctl daemon-reload
sudo systemctl restart docker
command line
-
get image from docker hub:
Image is a pre-built system to run in docker.
docker pull user/repo[:tag]
-
list local images:
# ls local images (top layer images) docker image ls # example otuput: # REPOSITORY TAG IMAGE ID CREATED SIZE # hello-world latest 301bfc30ea2b 43 hours ago 13.3kB # ls dangling images (deprecated images, displayed as <none>:<none>) docker image ls -f dangling=true # auto clean dangling images docker image prune # ls all local images (including middle layer images/dependencies, do not delete them!) docker image ls -a # ls matched repos docker image ls ubuntu # only ls IMAGE ID (can be used with rm) docker image ls -q
-
remove local images
# rm a local image docker image rm <name[:tag]/image_ID> # rm all images named ubuntu* docker image rm $(docker image ls -q ubuntu)
-
run & manage container
Container is the instantiation of an image.
### run a command and exit. docker run ubuntu:18.04 /bin/echo "Hello!" ### run interactive container # --interactive, --tty. should follow nothing, or a specific shell docker run -it ubuntu:18.04 [/bin/bash] # enter shell echo "Hello!" exit # exit shell # auto remove the container when exit docker run --rm -it ubuntu:18.04 ### run detached container # the command itself must be non-terminating! docker run -d [--name test] ubuntu:18.04 /bin/bash -c "while true; do echo hello world; sleep 1; done" # will print the <ID> of this container # this will still exit after finishing the command docker run -d ubuntu:18.04 /bin/bash -c "echo hello world" # detached interactive: docker run -dit ubuntu:18.04 ### port mapping (--publish) # single port mapping docker run -d -p <host>:<container> nginx # random port mapping docker run -d -P nginx # use host network (all ports are mapped identitcally) docker run --network host nginx ### volume mapping (--volume) docker run -v /host:/container -it ubuntu ### docker container # only ls running containers docker container ls # example output: # CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES # if name not assigned, it will be randomly generated like `elastic_knuth` # ls all containers (recently stopped) docker container ls -a # print logs from detached container docker container logs <ID/name> # terminate a running container docker container stop <ID/name> # start a terminated container docker container start <ID/name> # restart a running container docker container restart <ID/name> ### attach to the current shell of a detached container docker attach <ID/name> # enter shell # if exit from this shell, the container will stop too. ### attach by entering a new shell. docker exec -it <ID/name> bash # enter shell # if exit from this shell, the container will not stop! ### remove terminated container docker container rm <ID/name> # auto remove all termintaed containers docker container prune
-
other utils
# ps of all running containers docker ps # ps of all containers docker ps -a # filtered ps docker ps --filter "name=nostalgic" # top docker top <ID/name>
-
commit to image
# run a detached nginx container docker run --name webserver -d -p 80:80 nginx # enter the shell of the container docker exec -it webserver bash # begin shell: modify the index file echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html exit # end shell # check changes by diff docker diff webserver # commit changes docker commit webserver nginx:v2 [--message "change index"] # check the new image docker image ls nginx # check history docker history nginx:v2 # now you can run this new image docker run --name webserver2 -d -p 81:81 nginx:v2
Never use commit to build a image! The image built from commit is a black box, and may contain many redundant layers.
Always use
Dockerfile
to build a image!
Dockerfile
To build an image, we should first create an empty folder and create a Dockerfile
:
mkdir repo
cd repo
vim Dockerfile
edit it:
FROM nginx
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
Then build it:
# image name is nginx, tag is v3, context is pwd
docker build -t nginx:v3 .
You can even build from github repository:
# the repo should contain a Dockerfile
docker build -t hello-world https://github.com/docker-library/hello-world.git#master:amd64/hello-world
Push to registry:
# login to your registry
docker login registry
# tag your image with the name in remote registry
docker image tag myimage:version registry/myname/myimage:version
# you should see both images (share the same ID)
docker image ls
# then push it
docker image push registry/myname/myimage:version
Detailed command:
-
from base image
FROM ubuntu FROM scratch
-
copy from the context folder to container
COPY [--chown=<user>:<group>] <srcfiles> <dstdir> # copy files # if dstdir (/myfiles/) do not exit, it will be created automatically. # the last / is necessary! COPY index.html /myfiles/ # copy folder is however tricky: by default it copy the content, not the folder itself COPY folder /myfiles/ # cp folder/* /myfiles/ COPY folder /myfiles/folder # cp folder /myfiles/
-
add: advanced copy, but not recommended.
it can automatically decompress files, download from URL, ...
# copy then `tar -zxvf` ADD ubuntu-xenial-core-cloudimg-amd64-root.tar.gz / # it can replace all usage of COPY COPY [--chown=<user>:<group>] <srcfiles> <dstdir>
-
run command when building the image
# run command RUN apt update \ && apt upgrade \ && apt install -y nginx # always clear the workspace! RUN set -x; buildDeps='gcc libc6-dev make wget' \ && apt-get update \ && apt-get install -y $buildDeps \ && wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \ && mkdir -p /usr/src/redis \ && tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \ && make -C /usr/src/redis \ && make -C /usr/src/redis install \ && rm -rf /var/lib/apt/lists/* \ && rm redis.tar.gz \ && rm -r /usr/src/redis \ && apt-get purge -y --auto-remove $buildDeps
Use RUN sparingly! Each RUN will create a layer, and we should use as less layer as possible.
-
run command when container start
# shell mode CMD <shell command> CMD echo hello # exec mode CMD ["file", "args", ...] # must use "", not '', since it will be converted to json. CMD ["bash", "-c", "echo hello"] # entrypoint mode # define the base command, all later args will be appended to it. # example: CMD [ "curl", "-s", "http://myip.ipip.net" ] # docker run myip [OK] # docker run myip -i [ERROR] # instead, if we use ENTRYPOINT [ "curl", "-s", "http://myip.ipip.net" ] # docker run myip [OK] # docker run myip -i [OK]
-
environment variables
# env: persistent, can still be used in running containers. ENV <key> <val> ENV <k1>=<v1> <k2>=<v2> ... ENV DEBUG=on RUN echo $DEBUG # arg: non-persistent, only available in dockerfile ARG <key>=<val> # only available in FROM ARG DOCKER_USERNAME=library FROM ${DOCKER_USERNAME}/alpine # should reassign ARG DOCKER_USERNAME=library RUN set -x ; echo ${DOCKER_USERNAME}
-
create a anonymous volume.
It is just a declaration to create the folder.
VOLUME <path> VOLUME /data
In practice, we need to use
-v
to replace these anonymous volumes to make data persistent.# this will use /host to replace to anonymous volume. docker run -it -v /host:/data ubuntu
-
expose port
It is just a declaration that the port can be opened.
EXPOSE <port> [<port2> ...]
In practice, we need to use
-p
to map the port.docker run -it -p 80:80 nginx
-
change work directory
# create the folder, and later commands will be executed here. WORKDIR <path> # default workdir is root dir WORKDIR / # example WORKDIR /a # current workdir is /a/ WORKDIR b # current workdir is /a/b/ RUN touch c # create file at /a/b/c
-
change user
# must create the user first RUN groupadd -r redis && useradd -r -g redis redis # change to user redis for later commands. USER redis RUN [ "redis-server" ]
-
add metadata
LABEL <key>=<val> ...